/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.bourre.log.PixlibStringifier;
import com.bourre.structures.Range;
import com.bourre.transitions.IFrameListener;
import com.kairos.motion.AbstractAnimation;
import com.kairos.motion.strategy.AdvancedLoopStrategy;
import com.kairos.motion.strategy.AdvancedReversedLoopStrategy;
import com.kairos.motion.strategy.IPlayStrategy;

/**
 * Extends capabilities of the {@code AdvancedAnimation} object.
 * 
 * <p>You can control the number of loops the animation play and the range
 * wherein it can play, whatsoever the way of the animation, by using
 * the {@code loopCount} and the {@code range} parameters.</p>
 * 
 * <p>For achieving its work the {@code AdvancedAnimation} use two
 * customs strategy that put together loop and noloop strategies.</p>
 * 
 * <p>The range parameter could be really usefull to deal with animated
 * sprites in games, thus you can browse in all frames without having 
 * to destroy and create {@code AdvancedAnimation} each time you want
 * to change the animation.</p>
 * 
 * @author  Cédric Néhémie
 * @see		IFrameListener
 * @see		RTObject
 * @see		AbstractAnimation
 */
class com.kairos.motion.AbstractAdvancedAnimation extends AbstractAnimation implements IFrameListener 
{
	private var _oPlayRange : Range;
	private var _nLoopCount : Number;
	private var _nLoopNum : Number;
	private var _nRangeLength : Number;
	private var _nRangeStart : Number;

	private static var ADVANCED_LOOP : IPlayStrategy = new AdvancedLoopStrategy();
	private static var ADVANCED_REVERSED_LOOP : IPlayStrategy = new AdvancedReversedLoopStrategy();
	
	private function AbstractAdvancedAnimation(  fps : Number, loopCount : Number, range : Range, r : Boolean  ) 
	{
		super( fps, true, r );
		setRange ( range );
		setLoopCount( loopCount );
		reset();
	}
	
	
	/**
	 * Reset the internal loop counter.
	 * 
	 * <p>The frame, onto witch the animation is, don't
	 * changed on a call, its just affect the loop counter,
	 * use the {@code reset} method to reset the animation.</p>
	 */
	public function resetCount() : Void
	{
		_nLoopNum = 0;
	}
	
	/**
	 * Reset the animation.
	 * 
	 * <p>If the object is running the animation just restart
	 * from the first or the last frame according to the object
	 * play mode.</p>
	 * 
	 * <p>The loop counter is also reset after the call.</p>
	 */
	 public function reset() : Void
	 {
	 	_n = 0;
	 	
	 	if( _r ) _f = _nRangeLength;
		else _f = 1;

		_frameChanged( _nRangeStart + _f );
		
	 	resetCount();
	 }
	
	/**
	 * Define the frame range wherein the animation can play.
	 * 
	 * <p>Looping animations automatically return to the first
	 * frame of the range when arriving to the right side of 
	 * the range.</p>
	 * 
	 * <p>Range must be contained in the original moveclip timeline
	 * range, if the passed-in range overlap the valid play range, 
	 * it will be crop to respect the timeline limits.</p>
	 * 
	 * @param	range	A valid {@code Range} object.
	 */
	public function setRange ( range : Range ) : Void
	{
		_oPlayRange = range ? range : new Range ( 0, _tf ); 
		
		if( _oPlayRange.max > _tf )
			_oPlayRange.max = _tf;
			
		if( _oPlayRange.min < 0 )
			_oPlayRange.min = 0;
			
		_nRangeLength = _oPlayRange.max - _oPlayRange.min;
		_nRangeStart = _oPlayRange.min;
	}
	
	/**
	 * Return the current play range.
	 * 
	 * <p>If no range have been defined, the returned range
	 * is the movieclip timeline range.</p>
	 * 
	 * @return	A valid {@code Range} object.
	 */
	public function getRange () : Range
	{
		return _oPlayRange;
	}
	
	/**
	 * Define the number of loop the {@code AdvancedMovieClipRT}
	 * will perform before its stop.
	 * 
	 * <p>When you pass 0 to the function, the animation will loop
	 * indefinitely.</p>
	 * 
	 * <p>If the passed-in number isn't a valid loop count 
	 * it's set to 0.</p>
	 * 
	 * @param	n	{@code Number} of animation loops before stop.
	 */
	public function setLoopCount ( n : Number ) : Void
	{
		_nLoopCount = n >= 0 ? n : 0;
	}
	
	/**
	 * Return the current loop count limit.
	 * 
	 * @return	{@code Number} of animation loops before stop.
	 */
	public function getLoopCount () : Number
	{
		return _nLoopCount;
	}

	/**
	 * Override the default animation control.
	 */
	private function _movePlayHead ( f : Number ) : Void
	{	
		if( _oS.isMotionFinished ( _f, _nRangeLength, f ) )
		{
			if( ++_nLoopNum == _nLoopCount )
	 		{
	 			_f =  _r ? 1 : _nRangeLength;
	 			_frameChanged ( _nRangeStart + _f );
	 			super.stop();		
	 			return; 			
	 		}
	 		else
	 		{
	 			_f = _oS.getNextFrame( _f, _nRangeLength , f );
	 			
		 		if( _nLoopNum + 1 == _nLoopCount )
		 		{	
		 			_oS = _r ? REVERSED_NO_LOOP : NO_LOOP;
		 		}
		 	}
		}
		else
		{
			_f = _oS.getNextFrame( _f, _nRangeLength , f );
		}
		
		_frameChanged ( _nRangeStart + _f ); 
	}
	
	/**
	 * Override the default strategy choice.
	 */
	private function _switchStrategy () : Void
	{
		if( _nLoopCount == 0 )
		{
			_oS = _r ? REVERSED_LOOP : LOOP;
		}
		else if( _nLoopCount == 1 )
		{
			_oS = _r ? REVERSED_NO_LOOP : NO_LOOP;
		}
		else
		{
			_oS = _r ? ADVANCED_REVERSED_LOOP : ADVANCED_LOOP;
		}
	}
	
	/**
	 * Return the String representation of the object. 
	 */	
	public function toString() : String
	{
		return PixlibStringifier.stringify( this );
	}
}